package client

import (
	"encoding/json"
	"fmt"
	"gitee.com/HuaLiLiDeDiDiao/amazon-spapi-sdk-go/auth"
	"gitee.com/HuaLiLiDeDiDiao/amazon-spapi-sdk-go/auth/models"
	"github.com/go-resty/resty/v2"
	"math/rand"
	"net/url"
	"time"
)

const (
	NA = "https://sellingpartnerapi-na.amazon.com"
	EU = "https://sellingpartnerapi-eu.amazon.com"
	FE = "https://sellingpartnerapi-fe.amazon.com"
)

type SPAPIClient struct {
	Token    auth.TokenResponse
	Endpoint string
}
type Errors struct {
	Code    string `json:"code"`
	Message string `json:"message"`
	Details string `json:"details,omitempty"`
}
type ErrorList struct {
	Errors []Errors `json:"errors"`
}

func (e ErrorList) Error() string {
	jsonData, err := json.Marshal(e)
	if err != nil {
		return err.Error()
	}
	return string(jsonData)
}
func NewSPAPIClient(c models.Config, region string) (*SPAPIClient, error) {
	var err error
	var token auth.TokenResponse
	if c.ClientID == "" || c.ClientSecret == "" || c.TokenURL == "" {
		return nil, fmt.Errorf("client ID, client secret, and token URL are required")
	}
	authClient := &auth.AuthClient{
		ClientID:     c.ClientID,
		ClientSecret: c.ClientSecret,
		TokenURL:     c.TokenURL,
		RefreshToken: c.RefreshToken,
		Scope:        c.Scope,
	}
	if c.AccessToken != "" && c.DueTime != 0 && (c.DueTime+c.ExpiresIn-120) >= time.Now().Unix() {
		token.AccessToken = c.AccessToken
		token.ExpiresIn = c.ExpiresIn
		token.RefreshToken = c.RefreshToken
		token.TokenType = "bearer"
	} else if c.RefreshToken != "" {
		token, err = authClient.GetAccessToken(c.RefreshToken)
	} else if c.Scope != "" {
		token, err = authClient.GetGrantlessAccessToken(c.Scope)
	} else {
		return nil, fmt.Errorf("RefreshToken or Scope is required")
	}
	endpoint := NA
	switch region {
	case "EU", "eu-west-1":
		endpoint = EU
	case "FE", "us-west-2":
		endpoint = FE
	}
	return &SPAPIClient{
		Token:    token,
		Endpoint: endpoint,
	}, err
}

// MakeRequest makes a signed request to SP-API
func (c *SPAPIClient) MakeRequest(method, path string, queryParams url.Values, body interface{}) (*resty.Response, error) {
	client := resty.New()
	// Build the full URI
	uri := fmt.Sprintf("%s%s?%s", c.Endpoint, path, queryParams.Encode())
	// Prepare the request
	body, err := json.Marshal(body)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request body: %w", err)
	}
	request := client.R().
		SetHeader("x-amz-access-token", c.Token.AccessToken).
		SetHeader("x-amz-date", time.Now().UTC().Format("20060102T150405Z")).
		SetHeader("user-agent", "SPAPI-SDK-Go/1.0").
		SetHeader("Content-Type", "application/json;charset=uft-8").
		SetBody(body)
	// Send the request based on the HTTP method
	var resp *resty.Response
	maxRetries := 5
	baseDelay := time.Second
	for attempt := 0; attempt < maxRetries; attempt++ {
		switch method {
		case "GET":
			resp, err = request.Get(uri)
		case "POST":
			resp, err = request.Post(uri)
		case "PUT":
			resp, err = request.Put(uri)
		case "DELETE":
			resp, err = request.Delete(uri)
		default:
			return nil, fmt.Errorf("unsupported method: %s", method)
		}
		// 网络错误直接返回
		if err != nil {
			return nil, fmt.Errorf("request failed: %w", err)
		}
		if resp.StatusCode() == 429 || resp.StatusCode() == 503 {
			// 计算当前等待时间（指数退避）
			delay := baseDelay * time.Duration(1<<attempt) // 2^retryCount * baseDelay
			// 添加随机抖动，防止请求拥堵
			jitter := time.Duration(rand.Intn(100)) * time.Millisecond
			time.Sleep(delay + jitter)
			continue // 继续重试
		}
		if resp != nil && resp.StatusCode()/100 != 2 {
			var errorLists ErrorList
			if err := json.Unmarshal(resp.Body(), &errorLists); err != nil {
				return nil, fmt.Errorf("failed to parse response: %w", err)
			}
			return nil, &errorLists
		} else {
			return resp, err
		}
	}
	return nil, fmt.Errorf("exceeded max retries due to quota limits")
}
